package com.cgs.query.service.impl

import com.cgs.query.domain.QueryDefine
import com.cgs.query.event.QueryDefineEvent
import com.cgs.query.exception.QueryException
import com.cgs.query.handler.*
import com.cgs.query.service.IQueryDefineService
import com.cgs.query.service.jpa.QueryDefineRepository
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.ApplicationEventPublisher
import org.springframework.data.domain.Page
import org.springframework.data.domain.PageImpl
import org.springframework.data.domain.PageRequest
import org.springframework.stereotype.Service

@Service
open class QueryDefineServiceImpl : IQueryDefineService, UserInfoHolderAware, SecurityAdapter {
    override fun findByIds(ids: Iterable<String>): List<QueryDefine>? {
        return repository.findAll(ids)
    }

    private lateinit var userHolder: UserInfoHolder
    @Autowired
    private lateinit var publisher: ApplicationEventPublisher
    @Autowired
    private lateinit var repository: QueryDefineRepository

    override fun findById(id: String): QueryDefine? = repository.findOne(id)

    override fun findByName(name: String) = repository.findByName(name)

    override fun findByProjectName(projectName: String) =
            repository.findByProjectName(projectName)

    @Permission
    override fun save(define: QueryDefine): QueryDefine {
        val origin = repository.findOne(define.id)
        if (origin == null || origin.name != define.name || origin.tags != define.tags) {
            exist(define)
        }
        return repository.save(define.apply { if (userId.isNullOrBlank()) userId = userHolder.getUserId() })
    }

    override fun save(define: Iterable<QueryDefine>) {
        repository.save(define)
    }

    /**
     * 对比标签
     */
    override fun exist(define: QueryDefine) {
        val tags = define.tags
        if (tags.isNotEmpty()) {
            tags.forEach { tag ->
                if (repository.findByTagsAndName(tag, define.name)?.any { it.id != define.id } == true) {
                    throw QueryException("定义已存在：$tag.${define.name}")
                }
            }
        } else {
            if (repository.findByName(define.name)?.filter { it.id == define.id }?.any { it.tags.isEmpty() } == true)
                throw QueryException("定义已存在：${define.name}")
        }
    }

    @Permission
    override fun delete(id: String) {
        repository.delete(id).apply {
            publisher.publishEvent(QueryDefineEvent(QueryDefineEvent.Type.DELETE).apply { defineId = id })
        }
    }

    override fun delete(defines: List<QueryDefine>) {
        repository.deleteInBatch(defines).apply {
            publisher.publishEvent(QueryDefineEvent(QueryDefineEvent.Type.BATCH_DELETE).apply { this.defines = defines })
        }
    }

    override fun findByNameAndProjectName(name: String, projectName: String): QueryDefine? =
            findByNameAndProjectName(name, projectName)

    override fun findByNameLike(pn: Int, ps: Int, defineName: String): Any? {
        return if (defineName.isBlank())
            findAll(pn, ps)
        else
            repository.findByNameLike("%$defineName%", PageRequest(pn, ps))
    }

    override fun findAll(pn: Int, ps: Int): Any? {
        return repository.findAll(PageRequest(pn, ps))
    }

    override fun findByTagNamesAndNameLike(pn: Int, ps: Int, tagNames: List<String>, defineName: String): Any? {
        return if (tagNames.isEmpty()) {
            findByNameLike(pn, ps, defineName)
        } else {
            if (defineName.isBlank()) {
                findByTagNames(pn, ps, tagNames)
            } else {
                single2Page(pn, ps, tagNames) {
                    repository.findByTagsAndNameLike(it, "%$defineName%")
                }
            }
        }
    }

    override fun findByTagNames(pn: Int, ps: Int, tagNames: List<String>): Page<QueryDefine>? {
        return single2Page(pn, ps, tagNames) {
            findByTagName(it)
        }
    }

    private fun <T> single2Page(pn: Int, ps: Int, list: List<String>, run: (String) -> List<T>?): Page<T>? {
        //intersect
        var data = setOf<T>()
        list.filter { it.isNotBlank() }.forEachIndexed { index, it ->
            val oneTagQueryDefine = run(it)
            if (oneTagQueryDefine == null || oneTagQueryDefine.isEmpty()) {
                return@forEachIndexed
            } else {
                data = if (index == 0) {
                    data.plus(oneTagQueryDefine)
                } else {
                    data.intersect(oneTagQueryDefine)
                }
            }
        }
        return PageImpl<T>(data.toMutableList(), PageRequest(pn, ps), data.size.toLong())
    }

    override fun findByTagName(tagName: String): List<QueryDefine>? {
        return repository.findByTags(tagName)
    }

    override fun setUserInfoHolder(userInfoHolder: UserInfoHolder) {
        userHolder = userInfoHolder
    }

    override fun adapter(methodName: String, args: Array<Any?>): PermissionObject? {
        return when (methodName) {
            "save" -> {
                val queryDefine = (args[0] as QueryDefine)
                return if (queryDefine.id.isNotBlank()) {
                    PermissionObject("").apply {
                        this.userId = queryDefine.userId
                        this.obj = queryDefine
                        this.type = "queryDefine"
                    }
                } else {
                    null
                }
            }
            "delete" -> {
                val queryDefine = findById(args[0] as String)
                PermissionObject("").apply {
                    this.userId = queryDefine?.userId ?: ""
                    this.obj = queryDefine
                    this.type = "queryDefine"
                }
            }
            else -> null
        }
    }
}